#pragma once 
#include <bits/stdc++.h>

template<typename T>
class SyncQueue {
private:
	bool IsFull() const {
		return m_queue.size() == m_maxSize;
	}

	bool IsEmpty() const {
		return m_queue.empty();
	}

public:
	SyncQueue(int maxSize) : m_maxSize(maxSize) {
	}

	void put(const T& x) {
		// std::lock_guard<std::mutex> locker(m_mutex);

        //! v3
        std::unique_lock<std::mutex> locker(m_mutex);

        //! v1
        //! 可能会出现虚假唤醒，因为其获得锁后不会再次检查条件
		// while (IsFull()) {
		// 	std::cout << "缓冲区满了，需要等待..." << std::endl;
		// 	m_notFull.wait(m_mutex);              //! 进入休眠并释放锁, 等待被唤醒
		// }

        //! v2 
        //! 避免虚假唤醒:  获取锁后会再次检查条件，如果条件不满足则释放锁继续休眠等待
        m_notFull.wait(locker, [this](){ return !this->IsFull();});

		m_queue.push_back(x);
		m_notEmpty.notify_one();                   //! 告诉别人已经不为空
	}

	void pop(T& x) {
		// std::lock_guard<std::mutex> locker(m_mutex);

        //! v3
        std::unique_lock<std::mutex> locker(m_mutex);
		
        //! v1
		// while (IsEmpty()) {
		// 	std::cout << "缓冲区空了，需要等待..." << std::endl;
		// 	m_notEmpty.wait(m_mutex);             //! 进入休眠等待, 并释放锁
		// }
        //! v2
        m_notEmpty.wait(locker, [this](){ return !this->IsEmpty();});

		x = m_queue.front();
		m_queue.pop_front();
		m_notFull.notify_one();                   //! 告诉其他人已经不满了
	}

	bool empty() {
		std::lock_guard<std::mutex> locker(m_mutex);
		return m_queue.empty();
	}

	bool full() {
		std::lock_guard<std::mutex> locker(m_mutex);
		return m_queue.size() == m_maxSize;
	}

	size_t size() {
		std::lock_guard<std::mutex> locker(m_mutex);
		return m_queue.size();
	}

	int count() {
		return m_queue.size();
	}

private:
	std::list<T> m_queue;                       // 真实队列缓冲区
	std::mutex   m_mutex;                       // 互斥锁
	// std::condition_variable_any m_notEmpty;  // 不为空的条件
	// std::condition_variable_any m_notFull;   // 未满的条件

    //! v3
    std::condition_variable m_notEmpty;         // 不为空的条件
	std::condition_variable m_notFull;          // 未满的条件
	int m_maxSize;                              //ͬ 队列大小
};